package com.example.essearchdemo.product;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import com.example.search.ProductIndexInitializer;
import jakarta.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/products")
@Validated
public class ProductSearchController {

    private final ElasticsearchClient client;
    private final ProductIndexService indexService;

    public ProductSearchController(ElasticsearchClient client, ProductIndexService indexService) {
        this.client = client;
        this.indexService = indexService;
    }

    @GetMapping("/ping")
    public Map<String, Object> ping() {
        return Map.of("ok", true, "timestamp", System.currentTimeMillis());
    }

    @GetMapping("/search")
    public List<Map<String, Object>> search(
            @RequestParam("q") String query,
            @RequestParam(value = "page", defaultValue = "0") @Min(0) int page,
            @RequestParam(value = "size", defaultValue = "10") @Min(1) int size
    ) throws IOException {

        Query q = Query.of(qb -> qb
                .multiMatch(m -> m
                        .query(query)
                        .fields(
                                "name^3",
                                "name.partial^2",
                                "category",
                                "color",
                                "size",
                                "attributes.brand",
                                "attributes.material",
                                "attributes.color",
                                "attributes.size"
                        )
                        .fuzziness("AUTO")
                        .minimumShouldMatch("70%")
                )
        );

        SearchRequest req = SearchRequest.of(s -> s
                .index(ProductIndexInitializer.INDEX)
                .from(page * size)
                .size(size)
                .query(q)
                .sort(SortOptions.of(o -> o.score(srt -> srt.order(SortOrder.Desc))))
        );

        SearchResponse<Map> resp = client.search(req, Map.class);
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> results = (List<Map<String, Object>>) (List<?>) resp.hits().hits().stream()
                .map(h -> (Map<String, Object>) h.source())
                .collect(Collectors.toList());
        return results;
    }

    @PostMapping("/index")
    public Map<String, Object> index(@RequestBody Product product) throws IOException {
        indexService.indexOne(product);
        return Map.of("ok", true);
    }

    @PostMapping("/bulk")
    public Map<String, Object> bulkIndex(@RequestBody List<Product> products) throws IOException {
        indexService.bulkIndex(products);
        return Map.of("ok", true, "count", products.size());
    }
}